/**
 * 递归地将对象的键转换为驼峰形式
 */
type CamelCaseKeys<T> =
  T extends Record<string, any>
    ? {
        [K in keyof T as K extends string ? CamelCase<K> : K]: CamelCaseKeys<
          T[K]
        >
      }
    : T

/**
 * 将字符串转换为驼峰形式的类型（简化版）
 */
type CamelCase<S extends string> = S extends `${infer P}_${infer Rest}`
  ? `${P}${Capitalize<CamelCase<Rest>>}`
  : S

/**
 * 递归地将对象的键转换为下划线形式
 */
type SnakeCaseKeys<T> =
  T extends Record<string, any>
    ? {
        [K in keyof T as K extends string ? SnakeCase<K> : K]: SnakeCaseKeys<
          T[K]
        >
      }
    : T

/**
 * 将字符串转换为下划线形式的类型
 */
type SnakeCase<S extends string> = S extends `${infer P}${infer Rest}`
  ? `${P extends Lowercase<P> ? '' : '_'}${Lowercase<P>}${SnakeCase<Rest>}`
  : S

/**
 * 转换对象的键为驼峰形式
 * @param data 要转换的对象或数组
 */
export const convertKeysToCamelCaseDeep = <
  T extends Record<string, any> | Array<Record<string, any>>
>(
  data: T
): CamelCaseKeys<T> => {
  if (Array.isArray(data)) {
    return data.map((item) =>
      convertKeysToCamelCaseDeep(item)
    ) as CamelCaseKeys<T>
  } else if (typeof data === 'object' && data !== null) {
    const convertedData = {} as CamelCaseKeys<T>
    for (const key in data) {
      if (data.hasOwnProperty(key)) {
        const camelCaseKey = key.replace(/_([a-z])/g, (match, p1) =>
          p1.toUpperCase()
        )
        ;(convertedData as any)[camelCaseKey] = convertKeysToCamelCaseDeep(
          data[key]
        )
      }
    }
    return convertedData
  } else {
    return data as CamelCaseKeys<T>
  }
}

/**
 * 转换对象的键为下划线形式
 * @param data 要转换的对象或数组
 */
export const convertKeysToSnakeCaseDeep = <
  T extends Record<string, any> | Array<Record<string, any>>
>(
  data: T
): SnakeCaseKeys<T> => {
  if (Array.isArray(data)) {
    return data.map((item) =>
      convertKeysToSnakeCaseDeep(item)
    ) as SnakeCaseKeys<T>
  } else if (typeof data === 'object' && data !== null) {
    const convertedData = {} as SnakeCaseKeys<T>
    for (const key in data) {
      if (data.hasOwnProperty(key)) {
        const snakeCaseKey = key.replace(
          /[A-Z]/g,
          (match) => `_${match.toLowerCase()}`
        )
        ;(convertedData as any)[snakeCaseKey] = convertKeysToSnakeCaseDeep(
          data[key]
        )
      }
    }
    return convertedData
  } else {
    return data as SnakeCaseKeys<T>
  }
}
